package cPanes;


import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;

import javax.swing.BorderFactory;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;

import core.PaintThread;


import uiComponents.ContextMenu;

/**
 * The class which all content panes should inherit. Functionally a CPane is a JPanel which captures mouse events when the Alt and Ctrl
 * modifiers are pressed, and performs the appropriate menu and movement functions. 
 */

public abstract class CPane extends JPanel implements MouseListener, MouseMotionListener{
	private String friendlyName = "cPane";
	private int x=0;
	private int y=0;
	private int width=100;
	private int height=100;
	private ContextMenu rClick;
	private int mx=0;
	private int my=0;
	private int hscale=0;
	private int vscale=0;
	private int oldh=0;
	private int oldw=0;
	
	/**
	 * Must be called by child constructors in order to add standard functionality. Also sets up appearance.
	 */
	public CPane(){
		rClick=new ContextMenu();
		this.setBackground(Color.white);
		this.setBounds(x, y, width, height);
		this.setVisible(true);
		this.addMouseListener(this);
		this.addMouseMotionListener(this);
		this.setBorder(BorderFactory.createLineBorder(Color.GRAY, 2));
		this.setLayout(null);
		this.setFocusable(true);
	}
	
	/**
	 * Should be overridden with a function that resizes the pane's contents.
	 * @param nwidth The new width of the CPane
	 * @param nheight The new height of the CPane
	 */
	abstract void resizeContents(int owidth, int oheight);
	/**
	 * Triggered whenever a JMenuItem is chosen which isn't already handled in ContextMenu.
	 * @param e The ActionEvent captured from the original menu action
	 */
	public abstract void menuActions(ActionEvent e);
	/**
	 * Triggered whenever a MouseEvent occurs without the Alt or Ctrl modifiers.
	 * @param e The original MouseEvent object
	 * @param type The type of MouseEvent, can be 'CLICKED', 'DRAGGED', 'PRESSED', 'RELEASED', 'MOVED', 'EXITED' or 'ENTERED'
	 */
	abstract void mouseActions(MouseEvent e, String type);
	/**
	 * Sets the unique properties of the object using a String returned from returnSave()
	 * @param s The String returned by returnSave()
	 */
	public abstract void openSave(String s);
	/**
	 * Returns a single String which contains the unique properties of the Pane. Should be readable by openSave().
	 */
	public abstract String returnSave();
	
	/**
	 * Returns the ContextMenu for this CPane.
	 * @return The ContextMenu belonging to this CPane.
	 */
	protected ContextMenu getMenu(){
		return rClick;
	}
	
	/**
	 * Gets the x co-ordinate of the CPane's position relative to the top of the window.
	 * @return The x co-ordinate of the CPane's position.
	 */
	public int getX(){
		return x;
	}
	/**
	 * Gets the y co-ordinate of the CPane's position relative to the left of the window.
	 * @return The y co-ordinate of the CPane's position.
	 */
	public int getY(){
		return y;
	}
	/**
	 * Gets the width of the CPane, in pixels.
	 * @return The width of the CPane.
	 */
	public int getWidth(){
		return width;
	}
	/**
	 * Gets the height of the CPane, in pixels.
	 * @return The height of the CPane.
	 */
	public int getHeight(){
		return height;
	}
	/**
	 * Sets the X position of the CPane relative to the top of the window.
	 * @param nx The new x position of the CPane.
	 * @return Returns true when the new x is valid (within the window).
	 */
	public boolean setX(int nx){
		if(this.getParent() != null){
			if (nx > 0 && nx < this.getParent().getWidth()-this.getWidth()){
				x=nx;
				this.setBounds(nx, y, width, height);
				return true;
			}
			else return false;
		}
		else {
		x = nx;
		this.setBounds(nx, y, width, height);
		return true;
		}
	}
	/**
	 * Sets the Y position of the CPane relative to the left of the window.
	 * @param ny The new Y position of the CPane.
	 * @return Returns true when the new Y is valid (within the window).
	 */
	public boolean setY(int ny){
		if(this.getParent() != null){
			if (ny > 0 && ny < this.getParent().getHeight()-this.getHeight()){
				y=ny;
				this.setBounds(x, ny, width, height);
				return true;
			}
			else return false;
		}
		else {
			y = ny;
			this.setBounds(x, ny, width, height);
		}
		return true;
	}
	/**
	 * Sets the width of the window. This triggers a repaint to make sure there are no graphic artifacts.
	 * @param nw The new width of the window.
	 * @return Returns true if the width is valid (all of the window is still visible)
	 */
	public boolean setWidth(int nw){
		width=nw;
		this.setSize(nw, height);
		//resizeContents(nw, height);
		PaintThread t = new PaintThread(this);
		t.start();
		return true;
	}
	/**
	 * Sets the height of the window. This triggers a repaint to make sure there are no graphic artifacts.
	 * @param nh The new height of the window.
	 * @return Returns true if the height is valid (all of the window is still visible)
	 */
	public boolean setHeight(int nh){
		height=nh;
		this.setSize(width, nh);
		//resizeContents(width, nh);
		PaintThread t = new PaintThread(this);
		t.start();
		return true;
	}
	/**
	 * Gets the "friendly" name, or the three letter name for the CPane type.
	 * 
	 * @return The friendly name
	 */
	public String getName(){
		return friendlyName;
	}
	/**
	 * Sets the friendly name of the pane, this is a three letter String describing the type of CPane.
	 * 
	 * @param n The String to set the friendly name to
	 */
	public void setName(String n){
		friendlyName=n;
	}
	/**
	 * Returns a copy of the object, suitable for putting in a new window. This should work for any new CPane types, provided they have 
	 * correct returnSave() and openSave() methods.
	 * @return An exact copy of this CPane
	 */
	public CPane getClone(){
		try {
			CPane c = this.getClass().newInstance();
			c.setX(this.getX());
			c.setY(this.getY());
			c.setWidth(this.getWidth());
			c.setHeight(this.getHeight());
			String s=this.returnSave();
			c.openSave(s);
			return c;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	/**
	 * Switches this CPane with the one above it in the rendering order, so this CPane will be visible in the event of overlap.
	 */
	public void moveUp(){
		Container p=this.getParent();
		int i=p.getComponentZOrder(this);
		if (p.getComponentZOrder(this) > 0){
			p.setComponentZOrder(this, i-1);
			PaintThread t = new PaintThread(this);
			t.start();
		}
	}
	/**
	 * Switches this CPane with the one below it in the rendering order, so this CPane will not be visible in the event of overlap.
	 */
	public void moveDown(){
		Container p=this.getParent();
		int i=p.getComponentZOrder(this);
		if (p.getComponentZOrder(this) < p.getComponentCount()-1){
			p.setComponentZOrder(this, i+1);
			PaintThread t = new PaintThread(this);
			t.start();
		}
	}
	/**
	 * Moves this CPane to the top of the rendering order, so this CPane will always be visible in the event of overlap.
	 */
	public void moveToTop(){
		Container p=this.getParent();
			p.setComponentZOrder(this, 0);
			PaintThread t = new PaintThread(this);
			t.start();
	}
	/**
	 * Moves this CPane to the bottom of the rendering order, so this CPane will never be visible in the event of overlap.
	 */
	public void moveToBottom(){
		Container p=this.getParent();
		p.setComponentZOrder(this, p.getComponentCount()-1);
		PaintThread t = new PaintThread(this);
		t.start();
	}
	/**
	 * Handles mouse presses, passes them to the child pane if there are no CTRL or ALT modifiers.
	 */
	public void mousePressed(MouseEvent e) {
		e.getComponent().requestFocus();
		if(e.isAltDown()){
			if (e.getX()<5){
				hscale=1;
				oldw=width;
			}
			else if (e.getX()>this.getWidth()-10){
				hscale=2;
				oldw=width;
			}
			if (e.getY()<5){
				vscale=1;
				oldh=height;
			}
			else if (e.getY()>this.getHeight()-10){
				vscale=2;
				oldh=height;
			}
		}
		if(!e.isAltDown() && !e.isControlDown()){
			mouseActions(e, "PRESSED");
		}
	}
	/**
	 * Handles mouse releases, passes them to the child pane if there are no CTRL or ALT modifiers.
	 */
	public void mouseReleased(MouseEvent e) {
		if(hscale >0 || vscale >0 ){
			this.resizeContents(oldw, oldh);
		}
		vscale=0;
		hscale=0;
		if(!e.isAltDown() && !e.isControlDown()){
		mouseActions(e, "RELEASED");
		}
	}	
	/**
	 * Handles mouse moves, passes them to the child pane if there are no CTRL or ALT modifiers.
	 */
	public void mouseMoved(MouseEvent e){
		mx=e.getXOnScreen();
		my=e.getYOnScreen();
		if(!e.isAltDown() && !e.isControlDown()){
			mouseActions(e, "MOVED");
		}
	}
	/**
	 * Handles mouse drags, passes them to the child pane if there are no CTRL or ALT modifiers.
	 */
	public void mouseDragged(MouseEvent e) {
		int tx = e.getXOnScreen();
		int ty = e.getYOnScreen();
		if(e.isAltDown()){
			if (hscale==1){
				this.setWidth(this.getWidth()-(tx-mx));
				this.setX(this.getX()+(tx-mx));
			}
			else if (hscale==2) this.setWidth(this.getWidth()+(tx-mx));
			if (vscale==1){
				this.setHeight(this.getHeight()-(ty-my));
				this.setY(this.getY()+(ty-my));
			}
			else if (vscale==2) this.setHeight(this.getHeight()+(ty-my));
			if (hscale==0 && vscale==0){
			this.setX(this.getX()+(tx-mx));
			this.setY(this.getY()+(ty-my));
			}
		}
		if(!e.isAltDown() && !e.isControlDown()){
			mouseActions(e,"DRAGGED");
		}
		mx=e.getXOnScreen();
		my=e.getYOnScreen();
	}
	/**
	 * Handles mouse clicks, passes them to the child pane if there are no CTRL or ALT modifiers.
	 */
	public void mouseClicked(MouseEvent e) {
		if(e.isControlDown()){
			rClick.show(this,e.getX(),e.getY());
		}
		if(!e.isAltDown() && !e.isControlDown()){
			mouseActions(e, "CLICKED");	
		}
	}
	/**
	 * Handles mouse exits, passes them to the child pane if there are no CTRL or ALT modifiers.
	 */
	public void mouseExited(MouseEvent e) {
		if(!e.isAltDown() && !e.isControlDown()){
			mouseActions(e,"EXITED");
		}
	}
	/**
	 * Handles mouse entrances, passes them to the child pane if there are no CTRL or ALT modifiers.
	 */
	public void mouseEntered(MouseEvent e) {
		mx=e.getXOnScreen();
		my=e.getYOnScreen();
		if(!e.isAltDown() && !e.isControlDown()){
			mouseActions(e,"ENTERED");
		}
	}
}


